package wiki.xsx.core.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.SerializationException;
import wiki.xsx.core.handler.*;

import java.util.Arrays;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * redis工具
 * @author xsx
 * @date 2019/4/8
 * @since 1.8
 */
@Slf4j
public class RedisUtil {

    /**
     * redis模板(用于对象)
     */
    private static final RedisTemplate REDIS_TEMPLATE = ApplicationContextUtil.getContext().getBean("redisTemplate", RedisTemplate.class);
    /**
     * redis模板(用于字符串)
     */
    private static final StringRedisTemplate STRING_REDIS_TEMPLATE = ApplicationContextUtil.getContext().getBean("stringRedisTemplate", StringRedisTemplate.class);

    /**
     * 获取字符串类型助手
     * @return
     */
    public static StringTypeHandler getStringTypeHandler() {
        return StringTypeHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
    }

    /**
     * 获取哈希类型助手
     * @return
     */
    public static HashTypeHandler getHashTypeHandler() {
        return HashTypeHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
    }

    /**
     * 获取列表类型助手
     * @return
     */
    public static ListTypeHandler getListTypeHandler() {
        return ListTypeHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
    }

    /**
     * 获取无序集合类型助手
     * @return
     */
    public static SetTypeHandler getSetTypeHandler() {
        return SetTypeHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
    }

    /**
     * 获取有序集合类型助手
     * @return
     */
    public static ZsetTypeHandler getZsetTypeHandler() {
        return ZsetTypeHandler.getInstance(REDIS_TEMPLATE, STRING_REDIS_TEMPLATE);
    }

    /**
     * 是否存在key(对象)
     * @since redis 1.0.0
     * @param key 键
     * @return 返回布尔值,存在true,不存在false
     */
    public static boolean hasKeyAsObj(String key){
        return REDIS_TEMPLATE.hasKey(key);
    }

    /**
     * 是否存在key(字符串)
     * @since redis 1.0.0
     * @param key 键
     * @return 返回布尔值,存在true,不存在false
     */
    public static boolean hasKey(String key){
        return STRING_REDIS_TEMPLATE.hasKey(key);
    }

    /**
     * 移除对象key
     * @since redis 1.0.0
     * @param keys 键
     * @return 返回移除数量
     */
    public static Long removeAsObj(String ...keys){
        return REDIS_TEMPLATE.delete(Arrays.asList(keys));
    }

    /**
     * 移除字符串key
     * @since redis 1.0.0
     * @param keys 键
     * @return 返回移除数量
     */
    public static Long remove(String ...keys){
        return STRING_REDIS_TEMPLATE.delete(Arrays.asList(keys));
    }

    /**
     * 移除存在的对象key
     * @since redis 4.0.0
     * @param keys 键
     * @return 返回移除数量
     */
    public static Long unlinkAsObj(String ...keys){
        return REDIS_TEMPLATE.unlink(Arrays.asList(keys));
    }

    /**
     * 移除存在的字符串key
     * @since redis 4.0.0
     * @param keys 键
     * @return 返回移除数量
     */
    public static Long unlink(String ...keys){
        return STRING_REDIS_TEMPLATE.unlink(Arrays.asList(keys));
    }

    /**
     * 设置对象过期时间
     * @since redis 1.0.0
     * @param key 键
     * @param timeout 过期时间
     * @param timeUnit 时间单位
     * @return 返回布尔值,成功true,失败false
     */
    public static boolean expireAsObj(String key, long timeout, TimeUnit timeUnit) {
        return REDIS_TEMPLATE.expire(key, timeout, timeUnit);
    }

    /**
     * 设置字符串过期时间
     * @since redis 1.0.0
     * @param key 键
     * @param timeout 过期时间
     * @param timeUnit 时间单位
     * @return 返回布尔值,成功true,失败false
     */
    public static boolean expire(String key, long timeout, TimeUnit timeUnit) {
        return STRING_REDIS_TEMPLATE.expire(key, timeout, timeUnit);
    }

    /**
     * 设置对象过期时间
     * @since redis 1.0.0
     * @param key 键
     * @param date 过期时间
     * @return 返回布尔值,成功true,失败false
     */
    public static boolean expireAtAsObj(String key, Date date){
        return REDIS_TEMPLATE.expireAt(key, date);
    }

    /**
     * 设置字符串过期时间
     * @since redis 1.0.0
     * @param key 键
     * @param date 过期时间
     * @return 返回布尔值,成功true,失败false
     */
    public static boolean expireAt(String key, Date date){
        return STRING_REDIS_TEMPLATE.expireAt(key, date);
    }

    /**
     * 获取对象过期时间
     * @since redis 1.0.0
     * @param key 键
     * @param timeUnit 时间单位
     * @return 返回对象过期时间
     */
    public static Long getExpireAsObj(String key, TimeUnit timeUnit){
        return REDIS_TEMPLATE.getExpire(key, timeUnit);
    }

    /**
     * 获取字符串过期时间
     * @since redis 1.0.0
     * @param key 键
     * @param timeUnit 时间单位
     * @return 返回字符串过期时间
     */
    public static Long getExpire(String key, TimeUnit timeUnit) {
        return STRING_REDIS_TEMPLATE.getExpire(key, timeUnit);
    }

    /**
     * 获取对象存储数据类型
     * @since redis 1.0.0
     * @param key 键
     * @return 返回对象存储数据类型
     */
    public static DataType getTypeAsObj(String key){
        return REDIS_TEMPLATE.type(key);
    }

    /**
     * 获取字符串存储数据类型
     * @since redis 1.0.0
     * @param key 键
     * @return 返回字符串存储数据类型
     */
    public static DataType getType(String key){
        return STRING_REDIS_TEMPLATE.type(key);
    }

    /**
     * 对象的键集合
     * @since redis 1.0.0
     * @param pattern 键规则
     * @return 返回对象键的集合
     */
    public static Set<Object> keysAsObj(String pattern){
        return REDIS_TEMPLATE.keys(pattern);
    }

    /**
     * 字符串的键集合
     * @since redis 1.0.0
     * @param pattern 键规则
     * @return 返回字符串键的集合
     */
    public static Set<String> keys(String pattern){
        return STRING_REDIS_TEMPLATE.keys(pattern);
    }

    /**
     * 对象的键存在的数量
     * @since redis 1.0.0
     * @param keys 键
     * @return 返回对象键的数量
     */
    public static Long keysCountAsObj(String ...keys){
        return REDIS_TEMPLATE.countExistingKeys(Arrays.asList(keys));
    }

    /**
     * 字符串的键存在的数量
     * @since redis 1.0.0
     * @param keys 键
     * @return 返回字符串键的数量
     */
    public static Long keysCount(String ...keys) {
        return STRING_REDIS_TEMPLATE.countExistingKeys(Arrays.asList(keys));
    }

    /**
     * 获取对象随机key
     * @since redis 1.0.0
     * @return 返回对象随机的键
     */
    public static Object randomKeyAsObj() {
        Object o;
        try {
            o = REDIS_TEMPLATE.randomKey();
        }catch(SerializationException e){
            o = null;
        }
        return o;
    }

    /**
     * 获取字符串随机key
     * @since redis 1.0.0
     * @return 返回字符串随机的键
     */
    public static String randomKey(){
        return STRING_REDIS_TEMPLATE.randomKey();
    }

    /**
     * 重命名对象key
     * @since redis 1.0.0
     * @param oldKey 旧key
     * @param newKey 新key
     * @return 返回布尔值,成功true,失败false
     */
    public static void renameAsObj(String oldKey, String newKey){
        REDIS_TEMPLATE.rename(oldKey, newKey);
    }

    /**
     * 重命名字符串key
     * @since redis 1.0.0
     * @param oldKey 旧key
     * @param newKey 新key
     * @return 返回布尔值,成功true,失败false
     */
    public static void rename(String oldKey, String newKey){
        STRING_REDIS_TEMPLATE.rename(oldKey, newKey);
    }

    /**
     * 重命名对象key如果存在
     * @since redis 1.0.0
     * @param oldKey 旧key
     * @param newKey 新key
     * @return 返回布尔值,成功true,失败false
     */
    public static boolean renameAsObjIfAbsent(String oldKey, String newKey) {
        return REDIS_TEMPLATE.renameIfAbsent(oldKey, newKey);
    }

    /**
     * 重命名字符串key如果存在
     * @since redis 1.0.0
     * @param oldKey 旧key
     * @param newKey 新key
     * @return 返回布尔值,成功true,失败false
     */
    public static boolean renameIfAbsent(String oldKey, String newKey){
        return STRING_REDIS_TEMPLATE.renameIfAbsent(oldKey, newKey);
    }

    /**
     * 持久化对象
     * @since redis 2.2.0
     * @param key 键
     * @return 返回布尔值,成功true,失败false
     */
    public static boolean persistAsObj(String key){
        return REDIS_TEMPLATE.persist(key);
    }

    /**
     * 持久化字符串
     * @since redis 2.2.0
     * @param key 键
     * @return 返回布尔值,成功true,失败false
     */
    public static boolean persist(String key){
        return STRING_REDIS_TEMPLATE.persist(key);
    }

    /**
     * 移动对象到指定数据库
     * @since redis 1.0.0
     * @param dbIndex 数据库索引
     * @param key 键
     * @return 返回布尔值,成功true,失败false
     */
    public static boolean moveAsObj(int dbIndex, String key){
        return REDIS_TEMPLATE.move(key, dbIndex);
    }

    /**
     * 移动字符串到指定数据库
     * @since redis 1.0.0
     * @param dbIndex 数据库索引
     * @param key 键
     * @return 返回布尔值,成功true,失败false
     */
    public static boolean move(int dbIndex, String key){
        return STRING_REDIS_TEMPLATE.move(key, dbIndex);
    }

    /**
     * 获取原有数据库索引并设置新索引
     * @since redis 1.0.0
     * @param dbIndex 数据库索引
     * @return 返回原有索引
     */
    public static int getAndSetDB(int dbIndex) {
        int index = 0;
        try{
            LettuceConnectionFactory factory = (LettuceConnectionFactory) REDIS_TEMPLATE.getConnectionFactory();
            index = factory.getDatabase();
            factory.setDatabase(dbIndex);
            REDIS_TEMPLATE.setConnectionFactory(factory);
            STRING_REDIS_TEMPLATE.setConnectionFactory(factory);
        }catch (Exception e){
            log.error("数据库切换失败");
        }
        return index;
    }

    /**
     * 获取spring redis模板
     * @return 返回对象模板
     */
    public static RedisTemplate getRedisTemplate() {
        return REDIS_TEMPLATE;
    }

    /**
     * 获取spring string redis模板
     * @return 返回字符串模板
     */
    public static StringRedisTemplate getStringRedisTemplate() {
        return STRING_REDIS_TEMPLATE;
    }
}
